package com.ifp.opengate.boot.task;

import java.net.SocketException;
import java.net.UnknownHostException;
import java.util.HashMap;
import java.util.Map;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.boot.web.context.WebServerInitializedEvent;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.ApplicationListener;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.stereotype.Component;

import com.alibaba.fastjson.JSON;
import com.ifp.core.connector.AbstractConnector;
import com.ifp.core.connector.AbstractRPCConnector;
import com.ifp.core.log.Trace;
import com.ifp.core.util.StringUtil;
import com.ifp.opengate.boot.bean.OpenGateRoute;
import com.ifp.opengate.boot.constants.Constants;
import com.ifp.opengate.boot.utils.DynamicRouteService;
import com.ifp.opengate.boot.utils.IPUtils;
import com.ifp.opengate.boot.utils.RedisUtils;
import com.ifp.opengate.boot.utils.RouteUtils;

/**
 * @Classname ConnectorCacheDataTask
 * @Description TODO
 * @Date 2019/7/9 11:40
 * @Created by zzh
 *          <p>
 *          zhuzh@belink.com
 *          </p>
 * @Version 1.0
 */
@Component
public class ConnectorCacheDataTask
    implements ApplicationRunner, ApplicationContextAware, ApplicationListener<WebServerInitializedEvent> {
    //
    @Autowired
    RedisUtils redisUtils;
    //
    // @Autowired
    // CacheHandler cacheHandler;
    //
    // @Autowired
    // private UniErrCodeUtils uniErrCodeUtils;
    int port;

    String env;

    String ip;

    public String getIp() {
        return ip;
    }

    ApplicationContext applicationContext;

    private static BeanDefinitionRegistry beanDefinitonRegistry = null;

    @Value("${protocol.group.id}")
    String protocolGroupId;

    @Value("${opengate_group_route}")
    String routeGroupRoute;

    @Value("${opengate_env_service}")
    String opengateEnvService;

    @Value("${opengate_group_api_connector}")
    String apiConnectorGroup;

    @Value("${opengate_group_unierrorcode}")
    String uniErrorCode;

    private Map<String, Map<Object, Object>> routeOfConnectorMap = new HashMap<String, Map<Object, Object>>();

    private Map<String, OpenGateRoute> routeOfMap = new HashMap<>(16);

    public Map<String, OpenGateRoute> getRouteOfMap() {
        return routeOfMap;
    }

    public Map<String, Map<Object, Object>> getRouteOfConnectorMap() {
        return routeOfConnectorMap;
    }

    /**
     * 项目启动后，执行连接服务和路由关系的初始化
     *
     * @param args
     * @throws Exception
     */
    @Override
    public void run(ApplicationArguments args) throws Exception {
        env = getEnv();
        Trace.log(Trace.MODULE_COMMON, Trace.LEVEL_INFO, "The project `s env is-------------->{}", env);
        initConnectorService();
        initRouteOfConnector();
        initRoute();
        // TODO 待验证initApiOfConnector 注释放开 initApiOfConnector 的方法
        initApiOfConnector();
        // initHealthTask();
        // initErrorCode();
    }

    /**
     * 初始化错误码
     */
    // private void initErrorCode() {
    // Map<Object, Object> map = redisUtils.hmget(uniErrorCode);
    // for (Map.Entry<Object, Object> entry : map.entrySet()) {
    // String uecKey = entry.getKey().toString();
    // try {
    // Object message = redisUtils.hmget(uecKey);
    // uniErrCodeUtils.setunifiedErrorCodeMapInfo(uecKey.substring(uecKey.indexOf("_")+1), message);
    // Trace.log(Trace.MODULE_COMMON, Trace.LEVEL_INFO,"统一错误码缓存{}添加成功", uecKey);
    // } catch (Exception e) {
    // Trace.logError(Trace.MODULE_CORE, "统一错误码缓存{}添加异常", uecKey);
    // }
    // }
    // }

    // private void initHealthTask() {
    // ScheduledThreadPoolExecutor threadPoolExecutor = new ScheduledThreadPoolExecutor(1);
    // threadPoolExecutor.scheduleAtFixedRate(() -> {
    // Trace.logInfo(Trace.MODULE_COMMON, "initHealth start ...");
    // Map<Object, Object> map = redisUtils.hmget(protocolGroupId);
    // if (StringUtil.hasText(env)) {
    // for (Map.Entry<Object, Object> entry : map.entrySet()) {
    // if ("00".equals(entry.getValue())) {
    // Map<Object, Object> hmget = redisUtils.hmget(entry.getKey().toString());
    // if (env.equals(hmget.get(Constants.CONNECTOR_ENV))) {
    // if (applicationContext.containsBean(String.valueOf(hmget
    // .get(Constants.CONNECTOR_CODE)) + hmget.get(Constants.CONNECTOR_SYSID))) {
    // redisUtils.hset(Constants.COMMICATION_PROTOCOL_HEALTH_STATUS, hmget.get(Constants.CONNECTOR_CODE)
    // + "_" + hmget.get(Constants.CONNECTOR_SYSID) + "_" + hmget.get(Constants.CONNECTOR_ENV) + "_" + this.ip + ":" +
    // port, String.valueOf(0), 60);
    // } else {
    // redisUtils.hset(Constants.COMMICATION_PROTOCOL_HEALTH_STATUS, hmget.get(Constants.CONNECTOR_CODE)
    // + "_" + hmget.get(Constants.CONNECTOR_SYSID) + "_" + hmget.get(Constants.CONNECTOR_ENV) + "_" + this.ip + ":" +
    // port, this.ip + ":" + port, 60);
    // }
    // }
    // }
    // }
    // }
    // // zy 60
    // }, 60, 60, TimeUnit.SECONDS);
    // }

    /**
     * 初始化通讯服务和路由关系
     */
    private void initRouteOfConnector() {
        Map<Object, Object> routeConnectorGroup = redisUtils.hmget(routeGroupRoute);
        for (Map.Entry<Object, Object> entry : routeConnectorGroup.entrySet()) {
            Map<Object, Object> hmget = redisUtils.hmget(entry.getKey().toString());
            routeOfConnectorMap.put(String.valueOf(entry.getKey()), hmget);
        }
    }

    /**
     * 初始化通讯服务和api关系
     */
    private void initApiOfConnector() {
        Map<Object, Object> apiConnectorGroupMap = redisUtils.hmget(apiConnectorGroup);
        for (Map.Entry<Object, Object> entry : apiConnectorGroupMap.entrySet()) {
            Map<Object, Object> hmget = redisUtils.hmget(entry.getKey().toString());
            routeOfConnectorMap.put(String.valueOf(entry.getKey()), hmget);
        }
    }

    /**
     * 初始化通讯服务，如果是gateway原生接口则不走通讯连接器
     */
    private void initConnectorService() throws ClassNotFoundException {
        Map<Object, Object> map = redisUtils.hmget(protocolGroupId);
        if (StringUtil.hasText(env)) {
            for (Map.Entry<Object, Object> entry : map.entrySet()) {
                if ("00".equals(entry.getValue())) {
                    Map<Object, Object> hmget = redisUtils.hmget(entry.getKey().toString());
                    addCommunicationService(hmget);
                }
            }
        }
    }

    public void addCommunicationService(Map<Object, Object> hmget) {
        try {
            if (env.equals(hmget.get(Constants.CONNECTOR_ENV))) {
                if ("rpc".equalsIgnoreCase((String)hmget.get(Constants.CONNECTOR_PROTOCOL))) {
                    initRpcConnector(hmget);
                } else {
                    initCommonConnector(hmget);
                }
                redisUtils.hset(Constants.COMMICATION_PROTOCOL_HEALTH_STATUS,
                    hmget.get(Constants.CONNECTOR_CODE) + "_" + hmget.get(Constants.CONNECTOR_SYSID) + "_"
                        + hmget.get(Constants.CONNECTOR_ENV) + "_" + this.ip + ":" + port,
                    String.valueOf(0), 60);
            }
        } catch (Exception ex) {
            Trace.log(Trace.MODULE_COMMON, Trace.LEVEL_INFO, "初始化 Connector 失败---" + ex.getMessage());
            Trace.log(Trace.MODULE_COMMON, Trace.LEVEL_INFO, "初始化 Connector 失败-------------->{}",
                hmget.get(Constants.CONNECTOR_CODE) + "_" + hmget.get(Constants.CONNECTOR_SYSID) + "_"
                    + hmget.get(Constants.CONNECTOR_ENV));

            redisUtils.hset(Constants.COMMICATION_PROTOCOL_HEALTH_STATUS,
                hmget.get(Constants.CONNECTOR_CODE) + "_" + hmget.get(Constants.CONNECTOR_SYSID) + "_"
                    + hmget.get(Constants.CONNECTOR_ENV) + "_" + this.ip + ":" + port,
                this.ip + ":" + port, 60);
        }
    }

    /**
     * 通过redis的服务列表，获取当前所属环境
     *
     * @return
     * @throws UnknownHostException
     */
    private String getEnv() throws UnknownHostException, SocketException {
        this.ip = IPUtils.getLocalIP();
        String localIpPort = this.ip + ":" + port;
        // String localIpPort = "127.0.0.1" + ":" + port;
        // String localIpPort = "172.18.156.5" + ":" + "8010";
        Map<Object, Object> envMap = redisUtils.hmget(opengateEnvService);
        String env = (String)envMap.get(localIpPort);
        // Trace.log(Trace.MODULE_COMMON, Trace.LEVEL_INFO, "The project `s ip : port is-------------->{}",
        // localIpPort);
        // if (Cat.isInitialized()) {
        // Transaction transaction = Cat.getProducer().newTransaction("Env.Info", "ip:port--->" + localIpPort + "
        // env--->" + env);
        // transaction.setStatus("0");
        // transaction.complete();
        // }
        return "1";
    }

    /**
     * 初始化rpc通讯连接器
     *
     * @param connectorInfo
     * @throws ClassNotFoundException
     */
    private void initRpcConnector(Map<Object, Object> connectorInfo) throws ClassNotFoundException {
        // 如果当前系统已经存在该通讯服务删除其中的订阅的初始化状态
        if (StringUtil.hasText(connectorInfo.get(Constants.CONNECTOR_CODE))
            && applicationContext.containsBean(String.valueOf(connectorInfo.get(Constants.CONNECTOR_CODE))
                + connectorInfo.get(Constants.CONNECTOR_SYSID))) {
            AbstractRPCConnector connector = (AbstractRPCConnector)applicationContext
                .getBean(String.valueOf(connectorInfo.get(Constants.CONNECTOR_CODE))
                    + connectorInfo.get(Constants.CONNECTOR_SYSID));
            Map<String, Boolean> rpcConsumerStateMap = connector.getRpcConsumerStateMap();
            if (!rpcConsumerStateMap.isEmpty()) {
                for (Map.Entry<String, Boolean> rpcConsumer : rpcConsumerStateMap.entrySet()) {
                    rpcConsumer.setValue(false);
                }
            }
        }
        initConnector(connectorInfo);
        AbstractRPCConnector connector = (AbstractRPCConnector)applicationContext.getBean(
            String.valueOf(connectorInfo.get(Constants.CONNECTOR_CODE)) + connectorInfo.get(Constants.CONNECTOR_SYSID));
        connector.initConnector();
    }

    /**
     * 普通通讯服务及初始化
     *
     * @param connectorInfo
     * @throws ClassNotFoundException
     */
    private void initCommonConnector(Map<Object, Object> connectorInfo) throws ClassNotFoundException {
        initConnector(connectorInfo);
        AbstractConnector connector = (AbstractConnector)applicationContext.getBean(
            String.valueOf(connectorInfo.get(Constants.CONNECTOR_CODE)) + connectorInfo.get(Constants.CONNECTOR_SYSID));
        connector.initConnector();
    }

    /**
     * 服务注册
     *
     * @param connectorInfo
     * @throws ClassNotFoundException
     */
    private void initConnector(Map<Object, Object> connectorInfo) throws ClassNotFoundException {
        Trace.logInfo(Trace.MODULE_COMMON, "初始化通信连接器{} start...", connectorInfo.get(Constants.CONNECTOR_CLASS));
        Class<?> clazz = Class.forName((String)connectorInfo.get(Constants.CONNECTOR_CLASS));
        BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder.genericBeanDefinition(clazz);
        beanDefinitionBuilder.addPropertyValue("ip", connectorInfo.get(Constants.CONNECTOR_IP));
        beanDefinitionBuilder.addPropertyValue("port", connectorInfo.get(Constants.CONNECTOR_PORT));
        beanDefinitionBuilder.addPropertyValue("beanId",
            String.valueOf(connectorInfo.get(Constants.CONNECTOR_CODE)) + connectorInfo.get(Constants.CONNECTOR_SYSID));
        beanDefinitionBuilder.addPropertyValue("beanName", connectorInfo.get(Constants.CONNECTOR_SYSTEMNAME));
        if (StringUtil.hasText(connectorInfo.get(Constants.CONNECTOR_RESPONSEFORMATER))
            && applicationContext.containsBean((String)connectorInfo.get(Constants.CONNECTOR_RESPONSEFORMATER))) {
            beanDefinitionBuilder.addPropertyReference("responseFormatter",
                (String)connectorInfo.get(Constants.CONNECTOR_RESPONSEFORMATER));
        }
        if (StringUtil.hasText(connectorInfo.get(Constants.CONNECTOR_RREQUESTFORMATER))
            && applicationContext.containsBean((String)connectorInfo.get(Constants.CONNECTOR_RREQUESTFORMATER))) {
            beanDefinitionBuilder.addPropertyReference("requestFormatter",
                (String)connectorInfo.get(Constants.CONNECTOR_RREQUESTFORMATER));
        }
        beanDefinitonRegistry.registerBeanDefinition(
            String.valueOf(connectorInfo.get(Constants.CONNECTOR_CODE)) + connectorInfo.get(Constants.CONNECTOR_SYSID),
            beanDefinitionBuilder.getBeanDefinition());
        Trace.logInfo(Trace.MODULE_COMMON, "初始化通信连接器{} end",
            String.valueOf(connectorInfo.get(Constants.CONNECTOR_CODE)) + connectorInfo.get(Constants.CONNECTOR_SYSID));
    }

    /**
     * 获取applicationContext容器
     *
     * @param applicationContext
     * @throws BeansException
     */
    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
        ConfigurableApplicationContext configurableApplicationContext =
            (ConfigurableApplicationContext)applicationContext;
        beanDefinitonRegistry = (BeanDefinitionRegistry)configurableApplicationContext.getBeanFactory();
    }

    /**
     * 删除系统容器对应的服务
     *
     * @param hmget
     */
    public void deleteService(Map<Object, Object> hmget) {
        String serviceCode = hmget.get(Constants.CONNECTOR_SYSID) + "_" + hmget.get(Constants.CONNECTOR_CODE) + "_"
            + hmget.get(Constants.CONNECTOR_ENV);
        deleteService((String)hmget.get(Constants.CONNECTOR_ENV), serviceCode);
    }

    public void deleteService(String envStr, String serviceCode) {
        String[] serviceStrArry = serviceCode.split("_");
        try {
            if (env.equals(envStr)) {
                beanDefinitonRegistry.removeBeanDefinition(serviceStrArry[1] + serviceStrArry[0]);
                redisUtils.hset(Constants.COMMICATION_PROTOCOL_HEALTH_STATUS,
                    serviceStrArry[1] + "_" + serviceStrArry[0] + "_" + serviceStrArry[2] + "_" + this.ip + ":" + port,
                    this.ip + ":" + port, 60);
            }
        } catch (Exception e) {
            if (applicationContext.containsBean(serviceCode)) {
                redisUtils.hset(Constants.COMMICATION_PROTOCOL_HEALTH_STATUS,
                    serviceStrArry[1] + "_" + serviceStrArry[0] + "_" + serviceStrArry[2] + "_" + this.ip + ":" + port,
                    String.valueOf(0), 60);
            }
            Trace.log(Trace.MODULE_TRACE, Trace.LEVEL_INFO, "Service delete fail :{}", e);
        }
    }

    /**
     * 获取启动时候监听端口
     *
     * @param event
     */
    @Override
    public void onApplicationEvent(WebServerInitializedEvent event) {
        port = event.getWebServer().getPort();
    }

    @Autowired
    DynamicRouteService service;

    @Value("${opengate_group_route_path}")
    String opengateGroupRoutePath;

    /**
     * 初始化路由
     */

    public void initRoute() {
        Map<Object, Object> keysMap = redisUtils.hmget(opengateGroupRoutePath);
        if (!keysMap.isEmpty()) {
            for (Map.Entry<Object, Object> entry : keysMap.entrySet()) {
                String route = (String)redisUtils.get((String)entry.getKey());
                OpenGateRoute openGateRoute = JSON.parseObject(route, OpenGateRoute.class);
                if (null != openGateRoute
                    && (env.equals(openGateRoute.getEnv()) || !StringUtil.hasText(openGateRoute.getEnv()))) {
                    service.add(RouteUtils.assembleRouteDefinition(openGateRoute));
                    this.routeOfMap.put(openGateRoute.getRouteId(), openGateRoute);
                }
            }
        }
    }
}
